package middleware

import (
	"fmt"
	"illuminant/util"
	"log"
	"time"

	jwt "github.com/appleboy/gin-jwt/v2"
	"github.com/gin-gonic/gin"
)

var identityKey = "id"

// TODO UserInfo 应该来自数据库的用户表, 这里只是为了演示
type UserInfo struct {
	ID       string
	Name     string
	Password string
}

type loginParam struct {
	UserName string `json:"username"`
	Password string `json:"password"`
}

type token struct {
	Id       string `json:"id"`
	UserName string `json:"username"`
}

func JwtMiddleware() *jwt.GinJWTMiddleware {

	authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{
		Realm:       "illuminant project",
		Key:         []byte("illuminant secret"),
		Timeout:     time.Hour * 24,
		MaxRefresh:  time.Hour * 24,
		IdentityKey: identityKey,
		PayloadFunc: func(data interface{}) jwt.MapClaims {
			if v, ok := data.(*token); ok {
				return jwt.MapClaims{
					"id":       v.Id,
					"username": v.UserName,
				}
			}
			return jwt.MapClaims{}
		},
		IdentityHandler: func(c *gin.Context) interface{} {
			claims := jwt.ExtractClaims(c)
			return &token{
				Id:       claims["id"].(string),
				UserName: claims["username"].(string),
			}
		},
		Authenticator: func(c *gin.Context) (interface{}, error) {
			var (
				err   error
				param loginParam
				user  *UserInfo
			)

			if err = c.BindJSON(&param); err != nil {
				return "", jwt.ErrMissingLoginValues
			}

			// TODO 这里切换成实际的用户验证
			user, err = func(username, password string) (*UserInfo, error) {
				if username == "user" && password == "password" {
					return &UserInfo{
						ID:       "1",
						Name:     "user",
						Password: "password",
					}, nil
				}

				return nil, fmt.Errorf("Login error\n")
			}(param.UserName, param.Password)
			if err != nil {
				return nil, jwt.ErrFailedAuthentication
			}

			return &token{
				Id:       user.ID,
				UserName: user.Name,
			}, nil
		},
		LoginResponse: func(c *gin.Context, status int, token string, expire time.Time) {

			fmt.Println("LoginResponse >>>>>>>>>>>>>>>>>>>>>")
			util.Success(c, "", map[string]interface{}{
				"status": status,
				"token":  token,
				"expire": expire.Format(time.RFC3339),
			})
		},
		RefreshResponse: func(c *gin.Context, status int, token string, expire time.Time) {
			util.Success(c, "", map[string]interface{}{
				"status": status,
				"token":  token,
				"expire": expire.Format(time.RFC3339),
			})
		},
		Unauthorized: func(c *gin.Context, status int, token string) {
			fmt.Println("Unauthorized >>>>>>>>>>>>>>>>>>>>>")
			if token == "用户名/密码 不正确" {
				util.Fail(c, util.AUTH_USRER_PASS_NOT_MATCH, token, map[string]interface{}{
					"status": -1,
					"token":  token,
				})
				return
			}

			util.Fail(c, util.AUTH_LOGIN_TIMEOUT, token, map[string]interface{}{
				"status": status,
				"token":  token,
			})
		},
		HTTPStatusMessageFunc: func(e error, c *gin.Context) string {
			if e == jwt.ErrFailedAuthentication {
				return "用户名/密码 不正确"
			}

			return "登录失效, 请重新登录"
		},

		// TokenLookup is a string in the form of "<source>:<name>" that is used
		// to extract token from the request.
		// Optional. Default value "header:Authorization".
		// Possible values:
		// - "header:<name>"
		// - "query:<name>"
		// - "cookie:<name>"
		// - "param:<name>"
		TokenLookup: "header: Authorization, query: token, cookie: jwt",
		// TokenLookup: "query:token",
		// TokenLookup: "cookie:token",

		// TokenHeadName is a string in the header. Default value is "Bearer"
		TokenHeadName: "Bearer",

		// TimeFunc provides the current time. You can override it to use another time value. This is useful for testing or if your server uses a different time zone than your tokens.
		TimeFunc: time.Now,
	})

	if err != nil {
		log.Fatal("JWT Error:" + err.Error())
	}

	return authMiddleware
}
